package ipc;

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;

import rddl.EvalException;
import rddl.RDDL;
import rddl.State;
import rddl.RDDL.DOMAIN;
import rddl.RDDL.INSTANCE;
import rddl.RDDL.LCONST;
import rddl.RDDL.LVAR;
import rddl.RDDL.NONFLUENTS;
import rddl.RDDL.PVAR_INST_DEF;
import rddl.RDDL.PVAR_NAME;
import rddl.policy.Policy;
import rddl.viz.StateViz;
import java.util.Arrays;


public class IPCSimulator extends ipc.Simulator<IPCState, IPCAction> {
	/*
	 *  protected S state_;

	    List of legal actions from current state.
	    protected List<A> legalActions_;

	    List of rewards in current state. 
	    protected int[] rewards_;
	    (non-Javadoc)
	 * @see edu.oregonstate.eecs.mcplan.Simulator#copy()
	 */	
	private int time = 0;
	public INSTANCE   _i;
	public NONFLUENTS _n;
	public DOMAIN     _d;
	public Policy     _p;
	public StateViz   _v;
	public Random     _rand;
	public boolean change = false;
	
	public IPCSimulator(RDDL rddl, String instance_name, long seed) throws Exception {
		super();
		
		_i = rddl._tmInstanceNodes.get(instance_name);
		if (_i == null)
			throw new Exception("Instance '" + instance_name + 
					"' not found, choices are " + rddl._tmInstanceNodes.keySet());
		
		_n = null;
		if (_i._sNonFluents != null)
			_n = rddl._tmNonFluentNodes.get(_i._sNonFluents);
		_d = rddl._tmDomainNodes.get(_i._sDomain);
		if (_n != null && !_i._sDomain.equals(_n._sDomain))
			throw new Exception("Domain name of instance and fluents do not match: " + 
					_i._sDomain + " vs. " + _n._sDomain);

		state_ = new IPCState();
//		System.out.println(_i._hmObjects.size());
		state_.init(_n != null ? _n._hmObjects : null, _i._hmObjects,  
				_d._hmTypes, _d._hmPVariables, _d._hmCPF,
				_i._alInitState, _n == null ? null : _n._alNonFluents,
				_d._alStateConstraints, _d._exprReward, _i._nNonDefActions);
		
		legalActions_ = new ArrayList<IPCAction>();
		rewards_ = new double[1];
		rewards_[0] = 0;
		this.setInitialState();
		this.time = 0;
		
		_rand = new Random(seed);
	}
	
	private void computeLegalActions()
	{
		this.legalActions_.clear();

		try {
			TreeMap<String, ArrayList<PVAR_INST_DEF>> amap = rddl.ActionGenerator.getLegalBoolActionMap(this.getState());
			for( ArrayList<PVAR_INST_DEF> act : amap.values() ){
				legalActions_.add(new IPCAction(act));
			}
		} catch (EvalException e) {
			Main.exceptionLog.log("exception in computelegalactions\n"+e.getMessage());
			System.exit(1);
		}
//		System.out.println("Time for compute legal actions " + (time-System.currentTimeMillis()));
	}

	private IPCSimulator(IPCSimulator sim){

		state_ = new IPCState();
		state_.init(sim._n != null ? sim._n._hmObjects : null, 
				sim._i._hmObjects,  
				sim._d._hmTypes, sim._d._hmPVariables, sim._d._hmCPF,
				sim._i._alInitState, sim._n == null ? null : sim._n._alNonFluents,
						sim._d._alStateConstraints, sim._d._exprReward, sim._i._nNonDefActions);
		state_.copy(sim.state_);
		
		this._i = sim._i;
		this._n = sim._n;
		this._d = sim._d;
		this._p = sim._p;
		this._v = sim._v;
		this._rand = sim._rand;
		this.legalActions_ = new ArrayList<IPCAction>();
		for( IPCAction a: sim.legalActions_)
			this.legalActions_.add(a);
		
		this.time = sim.time;
		rewards_ = new double[getNumberOfAgents()];
		rewards_[0] = sim.rewards_[0];
//		System.out.println("Time for copying sim  " + (time-System.currentTimeMillis()));
	}
	
	@Override
	public IPCSimulator copy() {
		return new IPCSimulator(this);
	}

	@Override
	public double[] getFeatureVector(IPCAction action) throws Exception {
		throw new Exception("Not yet!");
	}

	@Override
	public int getNumberOfAgents() {
		return 1;
	}

	@Override
	public void setInitialState() {

		state_.init(_n != null ? _n._hmObjects : null, _i._hmObjects,  
					_d._hmTypes, _d._hmPVariables, _d._hmCPF,
					_i._alInitState, _n == null ? null : _n._alNonFluents,
					_d._alStateConstraints, _d._exprReward, _i._nNonDefActions);
		
		this.time = 0;
		this.resetRewards();
		this.computeReward(this._rand);

//		System.out.println("Time for set init  " + (time-System.currentTimeMillis()));
	}

	public void resetRewards(){
		for( int i = 0 ; i < rewards_.length; ++i )
			rewards_[i] = 0;
	}



	@Override
	public void setState(IPCState state) {

		this.state_.copy(state);
		this.computeReward(this._rand);
	}

	@Override
	public void setState(IPCState state, List<IPCAction> legalActions) {
		this.state_.copy(state);
		this.legalActions_.clear();
		for( IPCAction a: legalActions)
			this.legalActions_.add(a);
		this.computeReward(this._rand);
//		System.out.println("time for set state  " + (time-System.currentTimeMillis()));
	}	

	@Override
	public void takeAction(IPCAction action) {
//		long t1 = System.currentTimeMillis();
		try {//			System.out.println(action.toString());
			state_.computeNextState(action.actions_, _rand);
		} catch (EvalException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch(ConcurrentModificationException e){
			e.printStackTrace();
			System.out.println(action.toString()+"###");
			System.exit(1);
		}
		
		
		try{
			state_.advanceNextState();
			this.computeReward(_rand);
			++time;
		}catch(EvalException e){
			Main.exceptionLog.log("exception in IPCSIm takeaction, you gave action\n"+action.toString()+"\n"+e.getMessage());
			System.exit(1);
		}		
//		System.out.println("time for taking action  " + (t1-System.currentTimeMillis()));
	}
	
	public void computeReward(Random _rand) {
		if(rewards_ == null)
			rewards_ = new double[1];
		try {
			double r = ((Number)state_._reward.sample(new HashMap<LVAR,LCONST>(), state_, _rand)).doubleValue();
			this.rewards_[0] += r;
//			if( r == 0 ){
//				System.err.println("zero immediate reward");
//			}
//			Main.activityLog.log("reward computed "+this.rewards_[0]);
		} catch (EvalException e) {
			Main.exceptionLog.log("exception in computereward\n"+e.getMessage());
			System.exit(1);
		}		
//		System.out.println("time for computing reward  " + (time-System.currentTimeMillis()));
	}

	@Override
	public String toString() {
		return this.state_.toString();
	}
	
	@Override
	public boolean isTerminalState(){
		return (time >= this._i._nHorizon);
	}

	@Override
	public void incTime() {
		++this.time;
	}
	
	@Override
	public double[] getRewards() {
		return Arrays.copyOf(this.rewards_, this.rewards_.length);
	}

	@Override
	public List<IPCAction> getLegalActions() {
		this.computeLegalActions();
		return super.getLegalActions();
	}
	
	@Override
	public int getTime() {
		return this.time;
	}

	@Override
	public void setTime(int time) {
		this.time = time;
	}
};
